(function() // 自执行函数
{
	var DOC = document;

	Ext.Element = function( element, forceNew )
	{
		var dom = typeof element == "string" ? DOC.getElementById(element) : element, id;

		if( !dom ) return null;

		id = dom.id;

		// 如果不是强制获取新的元素
		// 并且elCache中有这个id的元素，那么就直接取到
		if( !forceNew && id && Ext.elCache[id] )
		{ // element object already exists
			return Ext.elCache[id].el;
		}

		this.dom = dom;
		this.id = id || Ext.id(dom);

		// //////////////////////////

		element =
		{
			id: '',
			dom: ''
		};

		new Ext.Element(document.getElementById('ab'), true);// <<<<--------
	};

	// 短命名
	var DH = Ext.DomHelper, El = Ext.Element, EC = Ext.elCache;

	El.prototype =
	{
		set: function( o, useSet )
		{
			var el = this.dom, attr, val, useSet = (useSet !== false) && !!el.setAttribute;

			for( attr in o )
			{
				if( o.hasOwnProperty(attr) )
				{
					val = o[attr];
					if( attr == 'style' )
					{
						DH.applyStyles(el, val);
					}
					else if( attr == 'cls' )
					{
						el.className = val;
					}
					else if( useSet )
					{
						el.setAttribute(attr, val);
					}
					else
					{
						el[attr] = val;
					}
				}
			}
			return this;
		},

		defaultUnit: "px",

		/**
		 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
		 * 
		 * @param {String} selector The simple selector to test
		 * @return {Boolean} True if this element matches the selector, else false
		 */
		is: function( simpleSelector )
		{
			return Ext.DomQuery.is(this.dom, simpleSelector);
		},

		// 尝试获得焦点，defer为延迟秒数
		focus: function( defer, /* private */dom )
		{
			var me = this, dom = dom || me.dom;
			try
			{
				if( Number(defer) )
				{
					me.focus.defer(defer, null,
					[
					    null, dom
					]);
				}
				else
				{
					dom.focus();
				}
			}
			catch (e)
			{
			}
			return me;
		},

		// 尝试移出焦点，异常将被忽略
		blur: function()
		{
			try
			{
				this.dom.blur();
			}
			catch (e)
			{
			}
			return this;
		},

		// 获得value属性，若asNumber为true，则进行转换，转换为int
		getValue: function( asNumber )
		{
			var val = this.dom.value;
			return asNumber ? parseInt(val, 10) : val;
		},

		// 添加事件，转交给EventManager
		addListener: function( eventName, fn, scope, options )
		{
			Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
			return this;
		},

		// 移除事件，转交给EventManager
		removeListener: function( eventName, fn, scope )
		{
			Ext.EventManager.removeListener(this.dom, eventName, fn, scope || this);
			return this;
		},

		// 移除所有事件，转交给EventManager
		removeAllListeners: function()
		{
			Ext.EventManager.removeAll(this.dom);
			return this;
		},

		/**
		 * Recursively removes all previous added listeners from this element and its children
		 * 
		 * @return {Ext.Element} this
		 */
		purgeAllListeners: function()
		{
			Ext.EventManager.purgeElement(this, true);
			return this;
		},
		/**
		 * @private Test if size has a unit, otherwise appends the default
		 */
		addUnits: function( size )
		{
			if( size === "" || size == "auto" || size === undefined )
			{
				size = size || '';
			}
			else if( !isNaN(size) || !unitPattern.test(size) )
			{
				size = size + (this.defaultUnit || 'px');
			}
			return size;
		},

		load: function( url, params, cb )
		{
			Ext.Ajax.request(Ext.apply(
			{
				params: params,
				url: url.url || url,
				callback: cb,
				el: this.dom,
				indicatorText: url.indicatorText || ''
			}, Ext.isObject(url) ? url : {}));
			return this;
		},

		isBorderBox: function()
		{
			return Ext.isBorderBox || Ext.isForcedBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
		},

		/**
		 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode}</p>
		 */
		remove: function()
		{
			var me = this, dom = me.dom;

			if( dom )
			{
				delete me.dom;
				Ext.removeNode(dom);
			}
		},
		hover: function( overFn, outFn, scope, options )
		{
			var me = this;
			me.on('mouseenter', overFn, scope || me.dom, options);
			me.on('mouseleave', outFn, scope || me.dom, options);
			return me;
		},

		/**
		 * Returns true if this element is an ancestor of the passed element
		 * 
		 * @param {HTMLElement/String} el The element to check
		 * @return {Boolean} True if this element is an ancestor of el, else false
		 */
		contains: function( el )
		{
			return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
		},

		/**
		 * Returns the value of a namespaced attribute from the element's underlying DOM node.
		 * 
		 * @param {String} namespace The namespace in which to look for the attribute
		 * @param {String} name The attribute name
		 * @return {String} The attribute value
		 * @deprecated
		 */
		getAttributeNS: function( ns, name )
		{
			return this.getAttribute(name, ns);
		},

		/**
		 * Returns the value of an attribute from the element's underlying DOM node.
		 * 
		 * @param {String} name The attribute name
		 * @param {String} namespace (optional) The namespace in which to look for the attribute
		 * @return {String} The attribute value
		 */
		getAttribute: (function()
		{
			var test = document.createElement('table'), isBrokenOnTable = false, hasGetAttribute = 'getAttribute' in test, unknownRe = /undefined|unknown/;

			if( hasGetAttribute )
			{

				try
				{
					test.getAttribute('ext:qtip');
				}
				catch (e)
				{
					isBrokenOnTable = true;
				}

				return function( name, ns )
				{
					var el = this.dom, value;

					if( el.getAttributeNS )
					{
						value = el.getAttributeNS(ns, name) || null;
					}

					if( value == null )
					{
						if( ns )
						{
							if( isBrokenOnTable && el.tagName.toUpperCase() == 'TABLE' )
							{
								try
								{
									value = el.getAttribute(ns + ':' + name);
								}
								catch (e)
								{
									value = '';
								}
							}
							else
							{
								value = el.getAttribute(ns + ':' + name);
							}
						}
						else
						{
							value = el.getAttribute(name) || el[name];
						}
					}
					return value || '';
				};
			}
			else
			{
				return function( name, ns )
				{
					var el = this.om, value, attribute;

					if( ns )
					{
						attribute = el[ns + ':' + name];
						value = unknownRe.test(typeof attribute) ? undefined : attribute;
					}
					else
					{
						value = el[name];
					}
					return value || '';
				};
			}
			test = null;
		})(),

		// update，不也是直接设置innerHTML么？我去
		update: function( html )
		{
			if( this.dom )
			{
				this.dom.innerHTML = html;
			}
			return this;
		}
	};

	var ep = El.prototype;

	El.addMethods = function( o )
	{
		Ext.apply(ep, o);
	};

	/**
	 * Appends an event handler (shorthand for {@link #addListener}).
	 * 
	 * @param {String} eventName The name of event to handle.
	 * @param {Function} fn The handler function the event invokes.
	 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
	 * @param {Object} options (optional) An object containing standard {@link #addListener} options
	 * @member Ext.Element
	 * @method on
	 */
	ep.on = ep.addListener;

	/**
	 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
	 * 
	 * @param {String} eventName The name of the event from which to remove the handler.
	 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
	 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added, then this must refer to the same
	 *        object.
	 * @return {Ext.Element} this
	 * @member Ext.Element
	 * @method un
	 */
	ep.un = ep.removeListener;

	/**
	 * true to automatically adjust width and height settings for box-model issues (default to true)
	 */
	ep.autoBoxAdjust = true;

	// private
	var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i, docEl;

	//
	El.get = function( el )
	{
		var ex, elm, id;

		// 如果没有传入el，那么返回null
		if( !el )
		{
			return null;
		}

		// 如果是字符串，那么就理解为ID
		if( typeof el == "string" )
		{ // element id
			if( !(elm = DOC.getElementById(el)) ) // 如果没有得到，return null
			{
				return null;
			}

			// 看看elCache里有没有
			if( EC[el] && EC[el].el )
			{
				ex = EC[el].el;
				ex.dom = elm;
			}
			else
			{

				// 此处，传入了一个新new出来的element，为什么不把this传过去呢？？
				ex = El.addToCache(new El(elm));
			}
			return ex;
		}

		// ////////////////////////////////////////

		// var d = document.getElementById('div');

		// Ext.get(d);

		// ////////////////////////////////////////
		// 如果是个HTMLElement
		else if( el.tagName )
		{ // dom element
			// 如果木有ID，那么就生成一个ID
			if( !(id = el.id) )
			{
				id = Ext.id(el);
			}

			// 缓存处理
			if( EC[id] && EC[id].el )
			{
				ex = EC[id].el;
				ex.dom = el;
			}
			else
			{
				ex = El.addToCache(new El(el));
			}
			return ex;
		}

		// 如果是Element对象
		else if( el instanceof El )
		{
			// 是document的话就直接返回
			if( el != docEl ) // 这个docEl应该是Ext封装的Ext.Element(document)
			{
				// refresh dom element in case no longer valid,
				// catch case where it hasn't been appended

				// If an el instance is passed, don't pass to getElementById without some kind of id
				// 如果是IE，并且没有id属性
				if( Ext.isIE && (el.id == undefined || el.id == '') )
				{
					el.dom = el.dom;
				}
				else
				{
					el.dom = DOC.getElementById(el.id) || el.dom;
				}
			}
			return el;
		}
		// 如果是组件
		else if( el.isComposite )
		{
			return el;
		}
		// 如果是数组，当作css选择器来处理
		else if( Ext.isArray(el) )
		{
			return El.select(el);
		}
		else if( el == DOC ) // 如果传入的是document
		{
			// create a bogus element object representing the document object
			if( !docEl ) // docEl不存在
			{
				var f = function()
				{
				};
				f.prototype = El.prototype;
				docEl = new f();
				docEl.dom = DOC;
			}
			return docEl;
		}
		return null;
	};

	// 将element添加到缓存
	El.addToCache = function( el, id )
	{
		id = id || el.id;

		Ext.elCache =
		{
			'box':
			{
				el: el,
				data: {},
				events: []
			},
			'box2':
			{
				el: el,
				data: {},
				events: []
			}
		};

		EC[id] =
		{
			el: el,
			data: {},
			events: {}
		};
		return el;
	};

	// private method for getting and setting element data
	El.data = function( el, key, value )
	{
		el = El.get(el);
		if( !el )
		{
			return null;
		}
		var c = EC[el.id].data;
		if( arguments.length == 2 )
		{
			return c[key];
		}
		else
		{
			return (c[key] = value);
		}
	};

	// private
	// Garbage collection - uncache elements/purge listeners on orphaned elements
	// so we don't hold a reference and cause the browser to retain them
	function garbageCollect()
	{
		if( !Ext.enableGarbageCollector )
		{
			clearInterval(El.collectorThreadId);
		}
		else
		{
			var eid, el, d, o;

			for( eid in EC ) // 遍历缓存中的元素
			{
				o = EC[eid];
				if( o.skipGC )
				{
					Ext.EventManager.removeFromSpecialCache(o.el);
					continue;
				}
				el = o.el;
				d = el.dom;
				// -------------------------------------------------------
				// Determining what is garbage:
				// -------------------------------------------------------
				// !d
				// dom node is null, definitely garbage
				// -------------------------------------------------------
				// !d.parentNode
				// no parentNode == direct orphan, definitely garbage
				// -------------------------------------------------------
				// !d.offsetParent && !document.getElementById(eid)
				// display none elements have no offsetParent so we will
				// also try to look it up by it's id. However, check
				// offsetParent first so we don't do unneeded lookups.
				// This enables collection of elements that are not orphans
				// directly, but somewhere up the line they have an orphan
				// parent.
				// -------------------------------------------------------
				if( !d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid)) )
				{
					if( Ext.enableListenerCollection )
					{
						Ext.EventManager.removeAll(d);
					}
					delete EC[eid];
				}
			}
			// Cleanup IE Object leaks
			if( Ext.isIE )
			{
				var t = {};
				for( eid in EC )
				{
					t[eid] = EC[eid];
				}
				EC = Ext.elCache = t;
			}
		}
	}
	El.collectorThreadId = setInterval(garbageCollect, 30000);

	var flyFn = function()
	{
	};
	flyFn.prototype = El.prototype;

	// 轻量级
	El.Flyweight = function( dom )
	{
		this.dom = dom;
	};

	El.Flyweight.prototype = new flyFn();
	El.Flyweight.prototype.isFlyweight = true;
	El._flyweights = {}; // 当作缓存使用，类似于Element.EC

	// 如果不传入named，那么就只缓存一个元素
	El.fly = function( el, named )
	{
		var ret = null;
		named = named || '_global';

		if( el = Ext.getDom(el) )
		{
			// 如果Ext._flyweights里没有，那么就把它的dom换成getDom获得的值
			(El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
			ret = El._flyweights[named];
		}
		return ret;
	};

	Ext.get = El.get;
	Ext.fly = El.fly;

	// speedy lookup for elements never to box adjust
	var noBoxAdjust = Ext.isStrict ?
	{
		select: 1
	} :
	{
		input: 1,
		select: 1,
		textarea: 1
	};
	if( Ext.isIE || Ext.isGecko )
	{
		noBoxAdjust['button'] = 1;
	}

})();
